package class02;

/**
 * @description: 只利用提供的随机机制，从[a,b] -> [c,d] 的随机
 * 此程序如何理解？ 疑问点：generateBinary getRandom 2021-7-5
 * 此程序正确执行了
 * @time: 2021/7/5 15:30
 * @author: lhy
 */
public class RandomToRandom {
    /**
     * 随机数生成类，这是唯一的随机机制，只能初始化并调用，不能修改
     */
    static class RandomBox{
        private final int MIN;
        private final int MAX;

        public RandomBox(int min, int max) {
            MIN = min;
            MAX = max;
        }
        public int random(){
            return MIN + (int) (Math.random() * (MAX-MIN+1));
        }
        public int getMin() {
            return MIN;
        }
        public int getMax() {
            return MAX;
        }
    }

    /**利用 RandomBox 这个唯一的随机机制，等概率得到 0 和 1
     * 0,1等概率生成器
     * @param randomBox 唯一可以利用的随机机制
     * @return 0 or 1
     */
    public static int generateBinary(RandomBox randomBox){
        int size = randomBox.getMax() - randomBox.getMin() + 1;
        int mid = size / 2;
        boolean isOdd = (size & 1) != 0;
        int ans;
        do {
            ans = randomBox.random() - randomBox.getMin();
        }while (ans == mid && isOdd);
        return ans < mid ? 0 : 1;
    }
    /**
     * 只借助 randomFun() 随机机制，等概率返回 [c,d]之间的随机数
     * @param from
     * @param to
     * @return [c,d]
     */
    public static int getRandom(RandomBox randomBox,int from, int to){
        if (from == to){
            return from;
        }
        int range = to - from;
        int numBinary = 1;
        while ((1 << numBinary) -1 < range) {
            numBinary++;
        }
        int ans;
        do {
            ans = 0;
            for (int i = 0; i < numBinary; i++) {
                ans |= (generateBinary(randomBox) << i);
            }
        }while (ans > range);
        return ans + from;
    }



    public static void main(String[] args) {
        int testTimes = 100000;

        System.out.println(">>> Test Random from [1,5] to [4,22]:");
        RandomBox randomBox = new RandomBox(1,5);
        int from = 4;
        int to = 22;
        int len = to + 1;
        int[] counts = new int[len];
        for (int i = 0; i < testTimes; i++) {
            int ans = getRandom(randomBox,from,to);
//            int ans = generateBinary(randomBox);
            counts[ans]++;
        }
        for (int i = 0; i < len; i++) {
            System.out.println("P("+ i + ") = " + (double) counts[i] / testTimes);
        }
        System.out.println("P(i) = " + 1.0 / (len-from));
    }
}
